package com.cz.android.sample.library.generate;

import android.content.Context;
import android.support.annotation.NonNull;
import android.util.ArrayMap;

import com.cz.android.sample.api.AndroidSampleConstant;
import com.cz.android.sample.api.item.CategoryItem;
import com.cz.android.sample.api.item.RegisterItem;

import java.util.ArrayList;
import java.util.List;

/**
 * Generate category by its package name.
 * For example, Here are our register sample list.
 * --------| class:com.cz.sample.dialog.DialogSample$DialogSample1
 * --------| class:com.cz.sample.dialog.DialogSample$DialogSample2
 * --------| class:com.cz.sample.component.ComponentDocumentSampleActivity
 * --------| class:com.cz.sample.component.ComponentListSampleActivity
 * --------| class:com.cz.sample.component.ComponentSampleFragment
 * --------| class:com.cz.sample.component.ComponentSourceSampleActivity
 * --------| class:com.cz.sample.custom.SampleFileTestFragment
 * --------| class:com.cz.sample.function.SamplePermissionActivity
 * --------| class:com.cz.sample.function.SamplePermissionFragment
 * --------| class:com.cz.sample.test.SampleDocumentFragment
 * --------| class:com.cz.sample.test.SampleMessageFragment
 * --------| class:com.cz.sample.test.SourceCodeSampleActivity
 *
 * The common package was：com.cz.sample
 * We use easily use the path of the class package to generate its category if necessary.
 * That will save us a lot of time organizing the sample structure ourselves.
 */
public class PackageCategoryGenerator implements SampleCategoryGenerator {

    @Override
    public List<CategoryItem> generate(@NonNull Context context, @NonNull List<RegisterItem> registeredItemList,@NonNull List<RegisterItem> generateItemList) {
        List<CategoryItem> categoryItemList=new ArrayList<>();
        //Step1: Analysis of the package name from all the registered sample classes.
        String packageName=null;
        String lastClassName=null;
        List<RegisterItem> registerItemList=new ArrayList<>();
        registerItemList.addAll(registeredItemList);
        registerItemList.addAll(generateItemList);
        //To avoid the real package name different from the package name in the context object.
        //We analyze the package by all the registered items.
        for (RegisterItem registerItem : registerItemList) {
            String className = registerItem.clazz.getName();
            if (null == lastClassName) {
                lastClassName = className;
            } else {
                String commonPackage = getCommonPackage(className, lastClassName);
                if (null == packageName || !packageName.equals(commonPackage)) {
                    packageName = commonPackage;
                }
            }
        }
        //Step2: generate all the categories by the class's package automatically
        ArrayMap<String,CategoryItem> packageCategoryList=new ArrayMap<>();
        for(RegisterItem registerItem:generateItemList){
            if(null!=packageName&&AndroidSampleConstant.CATEGORY_ROOT.equals(registerItem.category)){
                //Use the default category that generated by the package.
                String className = registerItem.clazz.getName();
                String simpleName = registerItem.clazz.getSimpleName();
                int end = className.lastIndexOf(simpleName);
                String path = className.substring(packageName.length() + 1, end-1);
                String[] pathArray = path.split("\\.");

                String category=AndroidSampleConstant.CATEGORY_ROOT;
                for(String str:pathArray){
                    CategoryItem categoryItem = packageCategoryList.get(str);
                    String title = capitalize(str);
                    if(null==categoryItem){
                        categoryItem=new CategoryItem(title,null,category);
                        packageCategoryList.put(str,categoryItem);
                        categoryItemList.add(categoryItem);
                    }
                    category=title;
                }
                registerItem.category=category;
            }
        }
        //If the category only has one sample existed. Make it point to the parent.
        for(RegisterItem registerItem:generateItemList){
            String category = registerItem.category;
            filterSingleCategoryItem(generateItemList, categoryItemList, registerItem, category);
        }
        return categoryItemList;
    }

    private void filterSingleCategoryItem(@NonNull List<RegisterItem> generateItemList, List<CategoryItem> categoryItemList, RegisterItem registerItem, String category) {
        CategoryItem categoryItem = getCategoryItem(categoryItemList, category);
        if(null!=categoryItem){
            List<RegisterItem> filterList = filterRegisterItem(generateItemList,registerItem, category);
            if(null==filterList){
                categoryItemList.remove(categoryItem);
                registerItem.category = categoryItem.category;
                filterSingleCategoryItem(generateItemList,categoryItemList,registerItem,categoryItem.category);
            }
        }
    }

    /**
     * Find one category item by the specific category name.
     * @param generateItemList
     * @param category
     */
    private CategoryItem getCategoryItem(@NonNull List<CategoryItem> generateItemList,@NonNull String category) {
        for(CategoryItem categoryItem:generateItemList){
            if(categoryItem.title.equals(category)){
                return categoryItem;
            }
        }
        return null;
    }

    /**
     * Filter the list by the given category.
     * @param registerItemList
     * @param category
     * @return
     */
    private List<RegisterItem> filterRegisterItem(@NonNull List<RegisterItem> registerItemList,@NonNull RegisterItem itself,@NonNull String category){
        List<RegisterItem> filterList=null;
        for(RegisterItem registerItem:registerItemList){
            if(registerItem!=itself&&registerItem.category.equals(category)){
                if(null==filterList){
                    filterList = new ArrayList<>();
                }
                filterList.add(registerItem);
            }
        }
        return filterList;
    }

    private String capitalize(String str) {
        if(str == null || str.isEmpty()) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    private String getCommonPackage(String className1,String className2){
        int i1=0,i2=0;
        String packageName=null;
        while(i1==i2){
            i1=className1.indexOf(".",i1+1);
            i2=className2.indexOf(".",i2+1);
            if(-1==i1||-1==i2){
                break;
            }
            if(className1.substring(0,i1).equals(className2.substring(0,i2))){
                //The two of the classes here have the same package.
                packageName=className1.substring(0,i1);
            }
        }
        return packageName;
    }
}
